<?php


class ProjectCliView
{
    //
    // Singleton pattern
    //
    /**
     * __construct
     * 
     * @author Pablo López Torres <pablolopeztorres@gmail.com> 
     * @return void
     */
    private function __construct()
    {
        // just to assure it is not instanciated from the outside 
    }

    /**
     * instance 
     * 
     * @var ProjectCliView
     * @access private
     */
    private static $instance;

    /**
     * getInstance
     * 
     * @author Pablo López Torres <pablolopeztorres@gmail.com> 
     * @return ProjectCliView
     */
    public static function getInstance()
    {
        if (! isset(self::$instance))
        {
            self::$instance = new self();
        }

        return self::$instance;
    }


	//
	// Constants
	//
	/**
	 * OPTION_SHOW_HIERARCHY 
	 * 
	 * @const string
	 */
	const OPTION_PLAIN = 'plain';

	/**
	 * OPTION_INVERT_HIERARCHY 
	 * 
	 * @const string
	 */
	const OPTION_INVERT_HIERARCHY = 'invert';


	/**
	 * printStatus
	 * 
	 * @access public
	 * @author Pablo López Torres <pablolopeztorres@gmail.com> 
	 * @param string $projectName 
	 * @param boolean $printName (default: TRUE)
	 * @return void
	 */
	public function printStatus($projectName, $printName = TRUE, $indent = 0)
	{
		if (! ProjectFactory::getInstance()->exists($projectName))
		{
			print("ERROR: The specified project does not exist.\n");
		}
		else
		{
			$indentString = str_repeat(' ', $indent * 4);

			$project = ProjectFactory::getInstance()->load($projectName);

			$projectName = $project->getName();
			$projectPath = $project->getPath();
			$projectFilenamesStatus = $project->getFilenamesStatus();
			$projectIndexedFiles = array_sum($projectFilenamesStatus);
			$projectFiles = count($projectFilenamesStatus);

			$projectClasses = count($project->getClassNames());

			if ($printName) {
				print($indentString . "Name: $projectName\n");
			}
			print($indentString . "Path: $projectPath\n");
			print($indentString . "$projectIndexedFiles / $projectFiles indexed files - $projectClasses classes \n");
		}
	}

	/**
	 * printClasses
	 * 
	 * @access public
	 * @author Pablo López Torres <pablolopeztorres@gmail.com> 
	 * @param mixed $projectName 
	 * @param mixed $options 
	 * @return void
	 */
	public function printClasses($projectName, $options = NULL)
	{
		$project = ProjectFactory::getInstance()->load($projectName);
		$classes = $project->getClasses();

		foreach ($classes as $class)
		{
			$line = CommandLineScript::getColor('light_blue') . $class->getName() . CommandLineScript::getColor('normal');

            if ($this->getOption(self::OPTION_PLAIN, $options) !== TRUE)
            {
                while ($class !== NULL)
                {
                    try
                    {
                        $class = $class->getParent();

                        if ($class !== NULL)
                        {
							if ($this->getOption(self::OPTION_INVERT_HIERARCHY, $options) !== TRUE)
                            {
                                $line = $class->getName() . CommandLineScript::getColor('light_gray') . " < " . CommandLineScript::getColor('normal') . $line;
                            }
                            else
                            {
                                $line .= CommandLineScript::getColor('light_gray') . " > " . CommandLineScript::getColor('normal') . $class->getName();
                            }
                        }
                    }
                    catch (UncatalogedElementException $e)
                    {
                        // prints the name of the parent and ends the hierarchy
                        if ($this->getOption(self::OPTION_INVERT_HIERARCHY, $options) !== TRUE)
                        {
                            $line = "*" . $class->getParentName() . CommandLineScript::getColor('light_gray') . " < " . CommandLineScript::getColor('normal') . $line;
                        }
                        else
                        {
                            $line .= CommandLineScript::getColor('light_gray') . " > " . CommandLineScript::getColor('normal') . "*" . $class->getParentName();
                        }
                        $class = NULL;
                    }
                }
            }

            print "$line\n";
		}
	}

	public function printClassInfo($projectName, $className, $options = NULL)
	{
		// access level
		$showPublicElements = TRUE;
		if ($this->getOption('only-public', $options) === TRUE)
		{
			$showProtectedElements = FALSE;
			$showPrivateElements = FALSE;
		}
		else
		{
			$showProtectedElements = TRUE;
			$showPrivateElements = TRUE;
		}


		$project = ProjectFactory::getInstance()->load($projectName);
		$class = $project->getClass($className);

		while ($class !== NULL)
		{

			// print class signature
			print CommandLineScript::getColor('light_gray') . ($class->isAbstract() ? " :@: " : " :%: ");
			print CommandLineScript::getColor('light_cyan');
			print $class->getName();
			print CommandLineScript::getColor('light_gray');

			if ($class->getParentName() !== NULL)
			{
				print " : ";

				print CommandLineScript::getColor('cyan');
				print $class->getParentName();
				print CommandLineScript::getColor('normal');
			}
			if (count($class->getImplementingNames()) > 0)
			{
				print " < " . CommandLineScript::getColor('cyan');

                $glue = CommandLineScript::getColor('normal') . ', ' . CommandLineScript::getColor('cyan');
                print implode($glue, $class->getImplementingNames());

                print CommandLineScript::getColor('normal');
			}

			print CommandLineScript::getColor('normal');

			print "\n";


			$sorted = 0x00;
			if ($this->getOption('sorted', $options) === TRUE)
			{
				$sorted |= PhpClass::ORDER_ALPHABETICALLY;
			}
			if ($this->getOption('sorted-access-level', $options) === TRUE)
			{
				$sorted |= PhpClass::ORDER_ACCESS_LEVEL;
			}

			foreach ($class->getElements($sorted, $this->getOption('group-parents', $options) === TRUE) as $child)
			{
                switch ($child['access'])
                {
                    case 'public':

                        if ($showPublicElements !== TRUE)
                        {
                            continue 2;
                        }

                        break;
                    case 'protected':

                        if ($showProtectedElements !== TRUE)
                        {
                            continue 2;
                        }

                        break;
                    case 'private':

                        if ($showPrivateElements !== TRUE)
                        {
                            continue 2;
                        }

                        break;
                    default:
                        throw new Exception('unexpected access level');
                        break;
                }

				$closers = $child['is_final'] ? '()' : '[]';

				if ($child['is_static'])
				{
					$closers = $closers[1] . $closers[0]; // reverse the chars
				}

				$useColorToPrintName = NULL;

				switch ($child['access'])
				{
				case 'public':
					$useColorToPrintName = CommandLineScript::getColor('light_red');
					$colorToUse = CommandLineScript::getColor('light_gray');
					$accessMark = '+';
					break;
				case 'protected':
					$useColorToPrintName = CommandLineScript::getColor('light_purple');
					$colorToUse = CommandLineScript::getColor('light_gray');
					$accessMark = '#';
					break;
				case 'private':
					$colorToUse = CommandLineScript::getColor('light_gray');
					$accessMark = '-';
					break;
				default:
					throw new Exception('wrong access');
					break;
				}

				print $colorToUse;

				// print method signature
				print "    " . $closers[0] . $accessMark . $closers[1] . " ";

				if (isset($useColorToPrintName))
				{
					print $useColorToPrintName;
				}
				print $child['name'];

				print $colorToUse;

                if ($this->getOption('break', $options) !== TRUE)
                {
                    print "(";
                }

				// print params
				foreach ($child['params'] as $key => &$param)
				{
                    if ($this->getOption('break', $options) === TRUE)
                    {
                        print "\n        ";
                    }

					if (isset($param['default'])) print "[ ";

					// if is a reference
					if ($param['reference'])
					{
						print "&";
					}

					print $param['name'];

					if (isset($param['default']))
					{
						print " = {$param['default']}";
					}
					if (isset($param['type']))
					{
						print " : {$param['type']}";
					}
					if (isset($param['default'])) print " ]";

					if (count($child['params']) > $key + 1)
					{
						if ($this->getOption('break', $options) !== TRUE)
                        {
                            print ", ";
                        }
					}
				}
                
				if ($this->getOption('break', $options) !== TRUE)
                {
                    print ")";
                }

				print "\n";
			}

			print CommandLineScript::getColor('normal');

			print "\n";

			if ($this->getOption('group-parents', $options) === TRUE)
			{
				$class = NULL;
			}
			else
			{
				try
				{
					$class = $class->getParent();
				}
				catch (UncatalogedElementException $e)
				{
					print "  +  Class '" . $class->getParentName() . "' is not indexed, so cannot be loaded.\n";
					$class = NULL;
				}

				if ($this->getOption('avoid-private-hiding', $options) !== TRUE)
				{
					// assure we're not going to see the private methods of the
					// parents
					$showPrivateElements = FALSE;
				}
			}
		}
	}

	private function getOption($optionField, $options)
	{
		$result = NULL;
		if (isset($options[$optionField]))
		{
			$result = $options[$optionField];
		}
		return $result;
	}
}
